package com.gjsrmyy.edi.config;

import com.gjsrmyy.edi.mapper.Mapper;
import lombok.extern.java.Log;
import org.apache.commons.io.IOUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

@Log
public enum MapperFactory {
    Edi {
        private SqlSessionFactory sqlSessionFactory;

        @Override
        public <T> T createMapper(Class<? extends Mapper> clazz) {
            return MapperFactory.createMapper(clazz, this);
        }

        @Override
        protected void createSqlSessionFactory() {
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream("dbConfig.xml");
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, this.name());
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                IOUtils.closeQuietly(inputStream);
            }
        }

        @Override
        public SqlSessionFactory getSqlSessionFactory() {
            return sqlSessionFactory;
        }

    };



    public abstract <T> T createMapper(Class<? extends Mapper> clazz);

    /**
     * Create SqlSessionFactory of environment
     */
    protected abstract void createSqlSessionFactory();

    /**
     * get SqlSessionFactory
     */
    public abstract SqlSessionFactory getSqlSessionFactory();

    static {
        Edi.createSqlSessionFactory();
    }

    @SuppressWarnings("unchecked")
    private static <T> T createMapper(Class<? extends Mapper> clazz, MapperFactory MapperFactory) {
        SqlSession sqlSession = MapperFactory.getSqlSessionFactory().openSession(true);
        Mapper mapper = sqlSession.getMapper(clazz);
        return (T) MapperProxy.bind(mapper, sqlSession);
    }

    /**
     * Mapper Proxy
     * executing mapper method and close sqlsession
     * @author boyce
     * @version 2014-4-9
     */
    private static class MapperProxy implements InvocationHandler {
        private Mapper mapper;
        private SqlSession sqlSession;

        private MapperProxy(Mapper mapper, SqlSession sqlSession) {
            this.mapper = mapper;
            this.sqlSession = sqlSession;
        }

        private static Mapper bind(Mapper mapper, SqlSession sqlSession) {
            return (Mapper) Proxy.newProxyInstance(mapper.getClass().getClassLoader(),
                    mapper.getClass().getInterfaces(), new MapperProxy(mapper, sqlSession));
        }

        /**
         * execute mapper method and finally close sqlSession
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object object = null;
            try {
                object = method.invoke(mapper, args);
                sqlSession.commit();
            } catch(Exception e) {
                sqlSession.rollback();
                e.printStackTrace();
            }
//            finally {
//                sqlSession.close();
//            }
            return object;
        }
    }
}
